/*
 * Copyright (C) 2021 Huawei Device Co., Ltd.
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.shashank.platform.shotangapp.view;

import com.shashank.platform.shotangapp.utils.AttrUtils;
import com.shashank.platform.shotangapp.utils.NumberUtil;
import ohos.agp.components.AttrSet;
import ohos.agp.components.Component;
import ohos.agp.components.DirectionalLayout;
import ohos.agp.render.*;
import ohos.agp.utils.Color;
import ohos.agp.utils.Point;
import ohos.agp.utils.RectFloat;
import ohos.app.Context;

/**
 * ShadowLayout阴影控件类
 */
public class ShadowLayout extends DirectionalLayout implements Component.EstimateSizeListener, Component.DrawTask {
    /**
     * 全部阴影
     */
    public static final int ALL = 0x1111;
    /**
     * 阴影左侧
     */
    public static final int LEFT = 0x0001;
    /**
     * 阴影上方
     */
    public static final int TOP = 0x0010;
    /**
     * 阴影右侧
     */
    public static final int RIGHT = 0x0100;
    /**
     * 阴影下方
     */
    public static final int BOTTOM = 0x1000;

    private static final float START = 0.1f;
    private static final float STOP = 0.5f;

    /**
     * 阴影的颜色
     */
    private int mShadowColor;

    /**
     * 阴影的大小范围
     */
    private float mShadowRadius = 0;

    /**
     * 阴影 x轴的偏移量
     */
    private float mShadowDx = 0;

    /**
     * 阴影 y轴的偏移量
     */
    private float mShadowDy = 0;

    /**
     * 阴影显示的边界
     */
    private int mShadowSide = ALL;

    /**
     * 阴影画笔
     */
    private final Paint mShadowPaint = new Paint();
    private final Paint mHelpPaint = new Paint();

    /**
     * 区域赋值
     */
    private final RectFloat mRectFloat = new RectFloat();

    private final Path path = new Path();

    /**
     * 构造
     *
     * @param context context
     */
    public ShadowLayout(Context context) {
        this(context, null);
    }

    /**
     * 有参构造
     *
     * @param context context
     * @param attrSet attrSet
     */
    public ShadowLayout(Context context, AttrSet attrSet) {
        this(context, attrSet, null);
    }

    /**
     * 有参构造
     *
     * @param context   context
     * @param attrSet   attrSet
     * @param styleName styleName
     */
    public ShadowLayout(Context context, AttrSet attrSet, String styleName) {
        super(context, attrSet, styleName);
        initialize(attrSet);
        setEstimateSizeListener(this);
        addDrawTask(this);
    }

    private void initialize(AttrSet attrs) {
        mShadowPaint.setDither(true);
        mShadowPaint.setAntiAlias(true);
        if (attrs != null) {
            mShadowRadius = AttrUtils.getFloat(attrs, "shadowRadius", 0f);
            mShadowColor = Color.getIntColor(AttrUtils.getString(attrs, "shadowColor", "#000000"));
            mShadowDx = AttrUtils.getFloat(attrs, "shadowDx", 0f);
            mShadowDy = AttrUtils.getFloat(attrs, "shadowDy", 0f);
            mShadowSide = AttrUtils.getInteger(attrs, "shadowSide", ALL);
        }
        initPaint();
    }

    /**
     * 初始化画笔属性
     */
    private void initPaint() {
        mHelpPaint.setColor(Color.TRANSPARENT);
        mHelpPaint.setStyle(Paint.Style.FILL_STYLE);
        mHelpPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        mShadowPaint.setColor(new Color(mShadowColor));
        mShadowPaint.setStyle(Paint.Style.FILL_STYLE);
        mShadowPaint.setStrokeCap(Paint.StrokeCap.ROUND_CAP);
        mShadowPaint.setStrokeJoin(Paint.Join.ROUND_JOIN);
        mShadowPaint.setMaskFilter(new MaskFilter(mShadowRadius, MaskFilter.Blur.OUTER));
    }

    /**
     * 测量方法
     *
     * @param widthMeasureSpec  宽
     * @param heightMeasureSpec 高
     * @return boolean
     * @noinspection checkstyle:NoWhitespaceBefore
     */
    @Override
    public boolean onEstimateSize(int widthMeasureSpec, int heightMeasureSpec) {
        float effect = mShadowRadius;
        float rectLeft = 0;
        float rectTop = 0;
        float rectRight = this.getEstimatedWidth();
        float rectBottom = this.getEstimatedHeight();
        int paddingLeft = 0;
        int paddingTop = 0;
        int paddingRight = 0;
        int paddingBottom = 0;
        this.getWidth();
        if ((mShadowSide & LEFT) == LEFT) {
            rectLeft = -effect;
            paddingLeft = (int) effect;
        }
        if ((mShadowSide & TOP) == TOP) {
            rectTop = -effect;
            paddingTop = (int) effect;
        }
        if ((mShadowSide & RIGHT) == RIGHT) {
            rectRight = (float) NumberUtil.add(this.getEstimatedWidth(), effect);
            paddingRight = (int) effect;
        }
        if ((mShadowSide & BOTTOM) == BOTTOM) {
            rectBottom = (float) NumberUtil.add(this.getEstimatedHeight(), effect);
            paddingBottom = (int) effect;
        }
        if (String.valueOf(mShadowDy) != null) {
            rectBottom = (float) NumberUtil.add(rectBottom, mShadowDy);
            paddingBottom = paddingBottom + (int) mShadowDy;
        }
        if (String.valueOf(mShadowDx) != null) {
            rectRight = (float) NumberUtil.add(rectRight, mShadowDx);
            paddingRight = paddingRight + (int) mShadowDx;
        }
        EstimateSpec.getSizeWithMode(
                EstimateSpec.getSize((int) NumberUtil.sub(rectRight, rectLeft)), EstimateSpec.PRECISE);
        EstimateSpec.getSizeWithMode(
                EstimateSpec.getSize((int) NumberUtil.sub(rectBottom, rectTop)), EstimateSpec.PRECISE);
        this.setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
        return false;
    }

    /**
     * 绘制
     *
     * @param component component
     * @param canvas    canvas
     */
    @Override
    public void onDraw(Component component, Canvas canvas) {
        int left = component.getLeft();
        int top = component.getTop();
        int right = component.getRight();
        int bottom = component.getBottom();
        double mRectFloatLeft = NumberUtil.sub(NumberUtil.add(left, mShadowRadius), mShadowDx);
        double mRectFloatTop = NumberUtil.sub(NumberUtil.add(top, mShadowRadius), mShadowDy);
        double mRectFloatRight = NumberUtil.sub(NumberUtil.sub(right, mShadowRadius), mShadowDx);
        double mRectFloatBottom = NumberUtil.sub(NumberUtil.sub(bottom, mShadowRadius), mShadowDy);
        mRectFloat.modify((float) mRectFloatLeft, (float) mRectFloatTop,
                (float) mRectFloatRight, (float) mRectFloatBottom);
        Point[] newPoints = getBoundPoint(mRectFloat);
        float[] newStops = new float[]{START, STOP};
        Color[] colors = new Color[]{new Color(mShadowColor), Color.TRANSPARENT};
        LinearShader shader = new LinearShader(newPoints, newStops, colors, Shader.TileMode.CLAMP_TILEMODE);
        mShadowPaint.setShader(shader, Paint.ShaderType.LINEAR_SHADER);
        mShadowPaint.setStrokeWidth(mShadowRadius);
        path.lineTo(mShadowRadius, mShadowRadius);
        path.lineTo(mShadowRadius, (float) NumberUtil.add(mRectFloat.getHeight(), mShadowRadius));
        path.lineTo((float) NumberUtil.add(mRectFloat.getWidth(), mShadowRadius),
                (float) NumberUtil.add(mRectFloat.getHeight(), mShadowRadius));
        path.lineTo((float) NumberUtil.add(mRectFloat.getWidth(), mShadowRadius), mShadowRadius);
        path.lineTo(mShadowRadius, mShadowRadius);
        path.close();
        canvas.drawRect(mRectFloat, mHelpPaint);
        canvas.drawPath(path, mShadowPaint);
    }

    /**
     * Set Alpha
     *
     * @param alpha alpha
     */
    @Override
    public void setAlpha(float alpha) {
        super.setAlpha(alpha);
        mShadowPaint.setAlpha(alpha);
    }

    /**
     * 获取绑定点
     *
     * @param rectFloat rectFloat
     * @return 绑定点
     */
    private Point[] getBoundPoint(RectFloat rectFloat) {
        Point point1 = new Point(rectFloat.left, rectFloat.top);
        Point point2 = new Point(rectFloat.left, rectFloat.bottom);
        Point point3 = new Point(rectFloat.right, rectFloat.bottom);
        Point point4 = new Point(rectFloat.right, rectFloat.top);
        return new Point[]{point1, point2, point3, point4};
    }
}
